﻿#region MyJScript - LGPL Copyright (c) 2008 Lionel Laské
//
// This file is part of MyJScript.
//
// MyJScript is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// MyJScript is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with MyJScript; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
//================================================================================
#endregion

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

using Microsoft.Scripting;
using Microsoft.Scripting.Hosting;
using Microsoft.Scripting.Ast;
using Microsoft.Scripting.Actions;
using Microsoft.Scripting.Runtime;
using Microsoft.Scripting.Shell;
using Microsoft.Scripting.Utils;
using Microsoft.Scripting.Helpers; // HACK: Defined locally, not in DLR today

using MyJScript.Compiler;
using MyJScript.Runtime;

namespace MyJScript.DLR
{
    /// <summary>
    /// DLR Context Class for MyJScript
    /// </summary>
    public class MJSLanguageContext : LanguageContext
    {
        /// <summary>
        /// Id for MyJScript
        /// </summary>
        static public readonly Guid MyJScriptGUID = new Guid("31FCE038-22FB-4e4d-87A1-FD479C666A4B");

        /// <summary>
        /// Constructor, call parent and add the binder for MyJScript.
        /// </summary>
        /// <param name="manager"></param>
        public MJSLanguageContext(ScriptDomainManager manager) : base(manager)
        {
            // Create binder
            Binder = new MJSBinder(new CodeContext(new Scope(this), this));

            // Create built-in functions
            MJSLibrary.Initialize();
        }

        /// <summary>
        /// Parse a source code and return an AST.
        /// </summary>
        /// <param name="context"></param>
        /// <returns>an AST for the code</returns>
        public override LambdaExpression ParseSourceCode(CompilerContext context)
        {
            // Call MyJScript parser
            Parser parser = new Parser(context.SourceUnit);
            parser.Parse();

            // Return the generated AST
            return parser.Result;
        }

        /// <summary>
        /// Unique identifier for MyJScript
        /// </summary>
        public override Guid LanguageGuid
        {
            get
            {
                return MyJScriptGUID;
            }
        }

        /// <summary>
        /// Language name
        /// </summary>
        public override string DisplayName
        {
            get { return "MyJScript"; }
        }

        /// <summary>
        /// Language version
        /// </summary>
        public override Version LanguageVersion
        {
            get { return new Version(0, 1, 0, 0); }
        }

        /// <summary>
        /// Lookup for a function or variable in the global context.
        /// </summary>
        /// <param name="context">code context</param>
        /// <param name="name">symbol to find</param>
        /// <param name="value">symbol value</param>
        /// <returns>true if found</returns>
        public override bool TryLookupGlobal(CodeContext context, SymbolId name, out object value)
        {
            // Look in MyJScript global context
            MJSObject mjscontext = MJSContext.GetContext();
            string memberName = SymbolTable.IdToString(name);
            if (mjscontext.HasMember(memberName))
            {
                value = mjscontext.Get(memberName);
                return true;
            }

            // Leave to DLR
            return base.TryLookupGlobal(context, name, out value);
        }

        /// <summary>
        /// Service implemented for the language.
        /// </summary>
        /// <typeparam name="ServiceType">service expected</typeparam>
        /// <param name="args">arguments.</param>
        /// <returns>the service</returns>
        public override ServiceType GetService<ServiceType>(params object[] args)
        {
            // Customized logo & prompt
            if (typeof(ServiceType) == typeof(CommandLine))
            {
                return (ServiceType)(object)new MJSCommandLine();
            }

            // Standard parser option
            else if (typeof(ServiceType) == typeof(OptionsParser))
            {
                return (ServiceType)(object)new DefaultOptionsParser(this);
            }
            
            return base.GetService<ServiceType>(args);
        }

        /// <summary>
        /// Format error.
        /// </summary>
        /// <param name="exception">exception occured</param>
        /// <returns>string with the right format for this exception</returns>
        public override string FormatException(Exception exception)
        {
            // Format syntax error
            if (exception is SyntaxErrorException)
            {
                // Display line and column
                SyntaxErrorException syntax = (SyntaxErrorException)exception;
                StringBuilder msg = new StringBuilder();
                msg.Append("(");
                msg.Append(syntax.Line);
                msg.Append(",");
                msg.Append(syntax.Column);
                msg.Append("): ");
                msg.Append(syntax.Message);
                return msg.ToString();
            }

            // Just display message
            else
                return exception.Message;
        }
    }
}
